/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/

using System;
using System.IO;
using System.IO.Packaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
using Telerik.Reporting;
using Telerik.Reporting.Processing;
using System.Linq;
using System.Globalization;
using System.Collections.Generic;

#if _LOCALDB
using Oceanside.LocalDB;
#else
using Oceanside.RemoteDB;
#endif
namespace Oceanside
{
    /// <summary>
    /// Interaction logic for ReportViewer.xaml
    /// </summary>
    public partial class ReportViewer : UserControl
    {
        /// <summary>
        /// Intended as a Singleton class.
        /// </summary>
        private ReportViewer()
        {
            InitializeComponent();
            this.documentViewer.FitToWidth();
            _singleton = this;
            this.Loaded += new RoutedEventHandler(ReportViewer_Loaded);
        }

        void ReportViewer_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateFormVisualState();
        }

        static ReportViewer()
        {
            _singleton = new ReportViewer();
        }

        static List<BUSINESS_DAY> _businessDaysList;
        private static Telerik.Reporting.Report _currentReport;
        public static Telerik.Reporting.Report CurrentReport
        {
            get { return _currentReport; }
            set
            {
                _currentReport = value;
                UpdateFormVisualState();
            }
        }

        private static CommonCalls.REPORT_TYPE_ENUM reportType;
        public static CommonCalls.REPORT_TYPE_ENUM ReportType
        {
            get { return reportType; }
            set { reportType = value; }
        }

        private static ReportViewer _singleton;
        public static ReportViewer Singleton
        {
            get { return ReportViewer._singleton; }
            set { ReportViewer._singleton = value; }
        }

        private static MemoryStream _renderMemoryStream;
        private static System.IO.Packaging.Package _package;
        private static Uri _packageUri;

        /// <summary>
        /// Used to enable and disable buttons when the form's state requires.
        /// </summary>
        private Button bPdf;
        private Button bExcel;
        private Button bCsv;
        private Button bWord;
        private Button bPower;
        private Button bBusinessDay;
        private Button bCalendar;

        public static void UpdateFormVisualState()
        {
            try
            {
                ///If one is null they are are null since they 
                ///are loaded from a template upon init.
                if (Singleton.bPdf != null)
                {
                    if (Singleton.documentViewer.Document != null)
                    {
                        Singleton.bPdf.IsEnabled = true;
                        Singleton.bExcel.IsEnabled = true;
                        Singleton.bCsv.IsEnabled = true;
                        Singleton.bPower.IsEnabled = true;
                        Singleton.bWord.IsEnabled = true;
                    }
                    else
                    {
                        Singleton.bPdf.IsEnabled = false;
                        Singleton.bExcel.IsEnabled = false;
                        Singleton.bCsv.IsEnabled = false;
                        Singleton.bWord.IsEnabled = false;
                        Singleton.bPower.IsEnabled = false;
                    }

                    if (CurrentReport is TelerikReports.EmployeeReport)
                    {
                        Singleton.bBusinessDay.IsEnabled = false;
                        Singleton.bCalendar.IsEnabled = false;
                    }
                    else
                    {
                        Singleton.bBusinessDay.IsEnabled = true;
                        Singleton.bCalendar.IsEnabled = true;
                    }
                }
            }
            catch (Exception) { }

        }

        /// <summary>
        /// Make sure that we clean up all memory trails while removing the report.
        /// </summary>
        public static void CleanMemory(bool disposeReport = true)
        {
            Singleton.documentViewer.Document = null;
            if (disposeReport)
            {
                if (_currentReport != null)
                {
                    try
                    {
                        _currentReport.Dispose();
                    }
                    catch { }
                }
            }
            if (_packageUri != null)
            {
                try
                {
                    PackageStore.RemovePackage(_packageUri);
                }
                catch { }
            }
            if (_renderMemoryStream != null)
            {
                try
                {
                    _renderMemoryStream.Dispose();
                }
                catch { }
            }
            UpdateFormVisualState();
        }


        #region NAVIGATION
        private void ButtonFirstPage_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Singleton.documentViewer.FirstPage();
            }
            catch { }

        }
        private void ButtonPreviousPage_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Singleton.documentViewer.PreviousPage();
            }
            catch { }
        }
        private void ButtonNextPage_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Singleton.documentViewer.NextPage();
            }
            catch { }
        }
        private void ButtonLastPage_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Singleton.documentViewer.LastPage();
            }
            catch { }
        }
        #endregion NAVIGATION

        #region RENDER
        private static RENDER_TYPE_ENUM _currentRenderType = RENDER_TYPE_ENUM.XPS;
        private static RENDER_ACTION _currentRenderAction = RENDER_ACTION.NONE;
        private static string _savePath = null;
        private enum RENDER_TYPE_ENUM
        {
            XPS,
            PDF,
            XLSX,
            PPTX,
            DOCX,
            CSV
        }
        private enum RENDER_ACTION
        {
            NONE,
            EMAIL,
            SAVE
        }
        private static void RenderThread()
        {
            try
            {
                if (_currentRenderType == RENDER_TYPE_ENUM.XPS)
                {
                    ReportProcessor reportProcessor = new ReportProcessor();
                    RenderingResult result = reportProcessor.RenderReport("XPS", _currentReport, null);
                    _renderMemoryStream = new MemoryStream(result.DocumentBytes);
                    _package = System.IO.Packaging.Package.Open(_renderMemoryStream);
                    string guidStr = "memorystream://" + Guid.NewGuid().ToString() + ".xps";
                    _packageUri = new Uri(guidStr);
                    PackageStore.AddPackage(_packageUri, _package);
                    XpsDocument doc = new XpsDocument(_package, CompressionOption.Fast, guidStr);
                    Constants.MainDispatcher.Invoke(new Action(() =>
                    {
                        try
                        {
                            Singleton.documentViewer.Document = doc.GetFixedDocumentSequence();
                            UpdateFormVisualState();
                            App.InvokeStopStoryBoard_NOTHROW();
                        }
                        catch (Exception ex)
                        {
                            Logging.LogException(ex);
                        }
                    }));
                }
                else
                {
                    ReportProcessor reportProcessor = new ReportProcessor();
                    RenderingResult result = reportProcessor.RenderReport(_currentRenderType.ToString(), _currentReport, null);

                    ///Add the report name to the path.

                    using (FileStream fs = new FileStream(_savePath, FileMode.Create))
                    {
                        fs.Write(result.DocumentBytes, 0, result.DocumentBytes.Length);
                        fs.Flush();
                        fs.Close();

                        if (_currentRenderAction == RENDER_ACTION.EMAIL)
                        {
                            if (!NetUtils.SendEmail(SHIFT.Current.User.EmailAddress, "Requested Report - NOREPLY - "
                                + DateTime.Now.ToShortTimeString(), "Attached you will find the report you requested.", _savePath))
                            {
                                UMD.ShowGuiMessageSTASafe_NOTHROW("The system was unable to email the report.  Please try again.");
                                File.Delete(_savePath);
                                return;
                            }
                            else
                            {
                                File.Delete(_savePath);
                            }

                        }

                        Constants.MainDispatcher.Invoke(new Action(() =>
                        {
                            try
                            {
                                App.InvokeStopStoryBoard_NOTHROW();
                                if (_currentRenderAction == RENDER_ACTION.SAVE)
                                {
                                    UMD.ShowGuiMessageSTASafe_NOTHROW("The requested report has been successfully exported and saved.");
                                }
                                else if (_currentRenderAction == RENDER_ACTION.EMAIL)
                                {
                                    UMD.ShowGuiMessageSTASafe_NOTHROW("The requested report has been successfully exported and emailed.");
                                }
                            }
                            catch (Exception ex)
                            {
                                Logging.LogException(ex);
                            }
                        }));
                    }
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        private static void StartRenderThread(RENDER_ACTION action, string path)
        {
            _currentRenderAction = action;
            _savePath = path;
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                try
                {
                    System.Threading.Thread newThread = new System.Threading.Thread(new System.Threading.ThreadStart(RenderThread));
                    newThread.SetApartmentState(System.Threading.ApartmentState.STA);
                    newThread.Start();
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                    UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
                }
            });
        }
        private void ButtonExportToPdf_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string path;
                RENDER_ACTION action;
                _currentRenderType = RENDER_TYPE_ENUM.PDF;
                if (RequestRenderAction(out path, out action))
                {
                    App.InvokeStartStoryBoard_NOTHROW();
                    StartRenderThread(action, path);
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        private void ButtonExportToExcel_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string path;
                RENDER_ACTION action;
                _currentRenderType = RENDER_TYPE_ENUM.XLSX;
                if (RequestRenderAction(out path, out action))
                {
                    App.InvokeStartStoryBoard_NOTHROW();
                    StartRenderThread(action, path);
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        private void ButtonExportToCSV_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string path;
                RENDER_ACTION action;
                _currentRenderType = RENDER_TYPE_ENUM.CSV;
                if (RequestRenderAction(out path, out action))
                {
                    App.InvokeStartStoryBoard_NOTHROW();
                    StartRenderThread(action, path);
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        private void ButtonExportToWord_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string path;
                RENDER_ACTION action;
                _currentRenderType = RENDER_TYPE_ENUM.DOCX;
                if (RequestRenderAction(out path, out action))
                {
                    App.InvokeStartStoryBoard_NOTHROW();
                    StartRenderThread(action, path);
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        private void ButtonExportToPowerpoint_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string path;
                RENDER_ACTION action;
                _currentRenderType = RENDER_TYPE_ENUM.PPTX;
                if (RequestRenderAction(out path, out action))
                {
                    App.InvokeStartStoryBoard_NOTHROW();
                    StartRenderThread(action, path);
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        private bool RequestRenderAction(out string path, out RENDER_ACTION action)
        {
            bool retValue = false;
            path = null;
            action = RENDER_ACTION.NONE;
            try
            {
                EmailOrSaveReport mb = new EmailOrSaveReport();
                mb.ShowDialog();
                if (EmailOrSaveReport.LastResponse != EmailOrSaveResponse.EXIT)
                {
                    if (EmailOrSaveReport.LastResponse == EmailOrSaveResponse.SAVE)
                    {
                        // Configure open file dialog box
                        Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
                        dlg.FileName = _currentReport.Name + "." + _currentRenderType.ToString().ToLower();
                        dlg.Filter = _currentRenderType.ToString().ToLower() + " | " + _currentRenderType.ToString().ToLower();
                        // Show open file dialog box
                        Nullable<bool> result = dlg.ShowDialog();

                        // Process open file dialog box results
                        if (result == true)
                        {
                            path = dlg.FileName;
                            action = RENDER_ACTION.SAVE;
                            retValue = true;
                        }
                    }
                    else
                    {
                        USER user = SHIFT.Current.User;
                        if (USER.IsValidEmailAddress(user.EmailAddress))
                        {
                            path = "C:\\MailMe\\" + _currentReport.Name + "." + _currentRenderType.ToString().ToLower();
                            action = RENDER_ACTION.EMAIL;
                            retValue = true;
                        }
                        else
                        {
                            YesNoBox ynb = new YesNoBox("The currently logged in user \"" + user.FirstName + " " + user.LastName + "\" does not have a valid email address set in their user profile.  If you would like \"" + user.FirstName + " " + user.LastName + "\" to receive reports via email, you must set a valid email address for this user.  Would you like to set an email address right now?");
                            ynb.ShowDialog();

                            if (YesNoBox.LastResponse == UserResponse.YES)
                            {
                                bool gotEmailOrExited = false;
                                do
                                {
                                    Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                                    Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Enter a Valid Email Address");
                                    Oceanside.FullKeyboard.ActiveKeyboard.ShowMe(null, false, true);
                                    if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                                    {
                                        if (USER.IsValidEmailAddress(Oceanside.FullKeyboard.GuiKeyPressedString))
                                        {
                                            SHIFT.GlobalContext.USERS.MergeOption = System.Data.Objects.MergeOption.OverwriteChanges;
                                            USER attahcedUser = SHIFT.GlobalContext.USERS.Where(U => U.Id == user.Id).First();
                                            attahcedUser.EmailAddress = Oceanside.FullKeyboard.GuiKeyPressedString;
                                            SHIFT.GlobalContext.SaveChanges();
                                            SHIFT.GlobalContext.USERS.MergeOption = System.Data.Objects.MergeOption.NoTracking;
                                            path = "C:\\MailMe\\" + _currentReport.Name + "." + _currentRenderType.ToString().ToLower();
                                            action = RENDER_ACTION.EMAIL;
                                            retValue = true;
                                            gotEmailOrExited = true;
                                        }
                                        else
                                        {
                                            YesNoBox nynb = new YesNoBox(Oceanside.FullKeyboard.GuiKeyPressedString + " does not appear to be a valid email address.  Would you like to try again?");
                                            nynb.ShowDialog();
                                            if (YesNoBox.LastResponse != UserResponse.YES)
                                            {
                                                gotEmailOrExited = true;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        gotEmailOrExited = true;
                                        UMD.ShowGuiMessageSTASafe_NOTHROW("No report data was sent via email.");
                                    }
                                } while (!gotEmailOrExited);
                            }
                            else
                            {
                                UMD.ShowGuiMessageSTASafe_NOTHROW("No report data was sent via email.");
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                UMD.ShowGuiMessageSTASafe_NOTHROW("An unexpected error occurred while preparing to generate your report.");
                Logging.LogException(ex);
            }
            return retValue;
        }
        #endregion RENDER

        #region FILTER

        private void ButtonBusinessDay_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                _businessDaysList = null;
                if (CurrentReport != null)
                {
                    SelectBusinessDayPopup pop = new SelectBusinessDayPopup(SHIFT.GlobalContext);
                    DateTime rightNow = DateTime.Now;
                    pop.SetMessage("Please select one or more business days from the list.  Today's calendar day is " + rightNow.DayOfWeek + " " + rightNow.Date.ToString("MM/dd/yy", CultureInfo.CreateSpecificCulture("en-US")) + ".  Please keep in mind that a business day can span multiple calendar days and a calendar day can contain multiple business days depending on when and how often you perform a house close-out.");
                    pop.ShowDialog();

                    if (SelectBusinessDayPopup.SelectedBusinessDays != null
                        && SelectBusinessDayPopup.SelectedBusinessDays.Count != 0
                        && !SelectBusinessDayPopup.DidExit)
                    {
                        CleanMemory(false);
                        _businessDaysList = SelectBusinessDayPopup.SelectedBusinessDays;
                        UpdateDataSource();
                    }
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
            }
        }

        private void ButtonCalender_Click(object sender, RoutedEventArgs e)
        {
            _businessDaysList = null;
            PromptForCalenderDaySpan();
        }

        private static void PromptForCalenderDaySpan()
        {
            try
            {
                if (CurrentReport != null)
                {
                    DateTimePicker dtm = new DateTimePicker(false, false, false, true, true, null);
                    dtm.ShowDialog();
                    if (DateTimePicker.LastResponse == UserResponse.YES)
                    {
                        if (DateTimePicker.LastEndSelectedDateTime >= DateTimePicker.LastStartSelectedDateTime)
                        {
                            CleanMemory(false);
                            DateTime now = DateTime.Now;

                            GetCalls.ReportStartDate = DateTimePicker.LastStartSelectedDateTime;
                            GetCalls.ReportEndDate = DateTimePicker.LastEndSelectedDateTime;

                            UpdateDataSource();
                        }
                        else
                        {
                            UMD.ShowGuiMessageSTASafe_NOTHROW("Please select an end date that is later than the report start date.");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
            }
        }
        #endregion FILTER

        #region HELPERS
        private static void InvokeSetContent()
        {
            Constants.MainDispatcher.Invoke(new Action(() =>
            {
                try
                {
                    foreach (ReportParameter rp in CurrentReport.ReportParameters)
                    {
                        if (rp.Name.Equals("StartDate"))
                        {
                            if (GetCalls.ReportStartDate == null)
                            {
                                rp.Value = DateTime.Now;
                            }
                            else
                            {
                                rp.Value = GetCalls.ReportStartDate;
                            }
                        }
                        else
                        {
                            if (GetCalls.ReportEndDate == null)
                            {
                                rp.Value = DateTime.Now;
                            }
                            else
                            {
                                rp.Value = GetCalls.ReportEndDate;
                            }
                        }
                    }
                    _currentRenderType = RENDER_TYPE_ENUM.XPS;
                    StartRenderThread(RENDER_ACTION.NONE, null);
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                    UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
                }
            }));
        }
        public static void UpdateDataSource()
        {
            App.InvokeStartStoryBoard_NOTHROW();
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                try
                {
                    if (CurrentReport is TelerikReports.EmployeeReport)
                    {
                        CurrentReport.DataSource = GetCalls.GetCurrentEmployees();
                    }
                    else if (CurrentReport is TelerikReports.PayrollReport)
                    {
                        CurrentReport.DataSource = GetCalls.GetEmployeePayroll(_businessDaysList);
                    }
                    else if (CurrentReport is TelerikReports.MasterSalesReport)
                    {
                        CurrentReport.DataSource = GetCalls.GetMasterSalesReport(_businessDaysList);
                    }
                    else if(ReportType <= CommonCalls.REPORT_TYPE_ENUM.SALES_DAYOFWEEK)
                    {
                        CurrentReport.DataSource = CommonCalls.GetSalesBy(_businessDaysList, ReportType);
                    }
                    else if (ReportType <= CommonCalls.REPORT_TYPE_ENUM.PROMO_BYEMPLOYEE)
                    {
                        CurrentReport.DataSource = GetCalls.GetDiscountsBy(_businessDaysList, ReportType);
                    }
                    InvokeSetContent();
                }
                catch (Exception ex)
                {
                    Logging.LogException(ex);
                    UMD.ShowGuiMessageSTASafe_NOTHROW("An error occurred while trying to render the requested report.");
                }
            });
        }
        #endregion

        #region SINCE USING TEMPLATE EASY WAY TO GET HANDLE

        private void ButtonExportToPdf_Initialized(object sender, EventArgs e)
        {
            bPdf = sender as Button;
        }

        private void ButtonExportToExcel_Initialized(object sender, EventArgs e)
        {
            bExcel = sender as Button;
        }

        private void ButtonExportToCSV_Initialized(object sender, EventArgs e)
        {
            bCsv = sender as Button;
        }

        private void ButtonExportToWord_Initialized(object sender, EventArgs e)
        {
            bWord = sender as Button;
        }

        private void ButtonExportToPowerpoint_Initialized(object sender, EventArgs e)
        {
            bPower = sender as Button;
        }

        private void ButtonMonth_Initialized(object sender, EventArgs e)
        {
            bBusinessDay = sender as Button;
        }

        private void ButtonCalender_Initialized(object sender, EventArgs e)
        {
            bCalendar = sender as Button;
        }
        #endregion
    }
}
